#include "adeptengine.h"

#include "../database.h"
#include "../random.h"

const char* AdeptEngine::ARG_DISCIPLINE = "discipline";

// Probability to raise an already known talent
const char* AdeptEngine::ARG_RAISE_KNOWN_TALENT_PROBABLITY = "raise_known_talent";
double AdeptEngine::ARG_RAISE_KNOWN_TALENT_PROBABLITY_DEFAULT = 0.7;

// Probability to learn a new talent
const char* AdeptEngine::ARG_LEARN_NEW_TALENT_PROBABLITY = "learn_new_talent";
double AdeptEngine::ARG_LEARN_NEW_TALENT_PROBABLITY_DEFAULT = 0.6;

// Probability to learn a new specialization
const char* AdeptEngine::ARG_LEARN_NEW_SPECIALIZATION_PROBABLITY = "learn_new_specialization";
double AdeptEngine::ARG_LEARN_NEW_SPECIALIZATION_PROBABLITY_DEFAULT = 0.5;

AdeptEngine::AdeptEngine()
  : NameGiverEngine()
{
  reset();
}

void AdeptEngine::init(const QVariantMap &args)
{
  if (_adept == nullptr) _adept = new Adept();
  _nameGiver = _adept;
  NameGiverEngine::init(args);

  Q_ASSERT(args.contains(ARG_DISCIPLINE));
  __ENGINE_CHECKARG__(ARG_RAISE_KNOWN_TALENT_PROBABLITY,_raiseKnownTalentProbability,Double);
  __ENGINE_CHECKARG__(ARG_LEARN_NEW_TALENT_PROBABLITY,_learnNewTalentProbability,Double);
  __ENGINE_CHECKARG__(ARG_LEARN_NEW_SPECIALIZATION_PROBABLITY,_learnNewSpecializationProbability,Double);

  _adept->set_discipline(Database::discipline(args[ARG_DISCIPLINE].toString()));
}

void AdeptEngine::reset()
{
  NameGiverEngine::reset();

  _adept = nullptr;
  _unknownTalents.clear();
  _knownTalents.clear();
}

void AdeptEngine::createBase()
{
  NameGiverEngine::createBase();

  _log << "[Talents] Attribution des valeurs de base" << _log.endl;
  _log << "[Talents] Passage de Rituel de karma au rang 1" << _log.endl;
  _adept->setTalentRank(Database::talent("Rituel de karma"),1);
  int creationPoints = 7;
  QList<Talent> availableTalents = _adept->discipline().talents(1);
  while (creationPoints > 0)
  {
    Talent t = availableTalents[Random::get<int>(0,availableTalents.size()-1)];
    int newRank = _adept->talentRank(t)+1;
    _log << "[Talents] Passage de " << t << " au rang " << newRank << _log.endl;
    _adept->setTalentRank(t, newRank);
    --creationPoints;
    if (newRank == 3) availableTalents.removeAll(t);
  }
}
bool AdeptEngine::canSpendLegendPoints()
{
  return !_knownTalents.isEmpty() || !_unknownTalents.isEmpty() || NameGiverEngine::canSpendLegendPoints();
}

void AdeptEngine::spendLegendPoints()
{
  if (!_knownTalents.isEmpty() && Random::check(_raiseKnownTalentProbability)) raiseKnownTalent();
  refreshLists();
  if (!_unknownTalents.isEmpty() && Random::check(_learnNewTalentProbability)) learnNewTalent();
  refreshLists();
  NameGiverEngine::spendLegendPoints();

  if (canRaiseCircle())
  {
    _adept->raiseCircle();
    _log << "Passage au cercle " << _adept->circle() << _log.endl;

  }
}

void AdeptEngine::refreshLists()
{
  NameGiverEngine::refreshLists();

  _unknownTalents.clear();
  for (int c=1;c<=_adept->circle();++c)
  {
    if (_adept->legendPoints() < Database::talentLegendCost(1,c)) break;
    foreach(const Talent& t, _adept->discipline().talents(c))
    {
      if (_adept->talentRank(t) == 0) _unknownTalents << t;
    }
  }

  _knownTalents.clear();
  foreach(const Talent& t, _adept->talents())
  {
    int rank = _adept->talentRank(t);
    if (rank < Database::MAXIMUM_TALENT_RANK && rank <= _adept->circle()+3)
    {
      int circle = _adept->discipline().talentCircle(t);
      if (_adept->legendPoints() >= Database::talentLegendCost(rank+1,circle)) _knownTalents << t;
    }
  }
}

void AdeptEngine::learnNewTalent()
{
  Talent t = _unknownTalents[Random::get<int>(0,_unknownTalents.size()-1)];
  _adept->setTalentRank(t,1);
  _adept->decreaseLegendPoints(Database::talentLegendCost(1,_adept->discipline().talentCircle(t)));
  _log << "[Talents] Passage du talent " << t << " au rang 1" << _log.endl;
}

void AdeptEngine::raiseKnownTalent()
{
  Talent t = _knownTalents[Random::get<int>(0,_knownTalents.size()-1)];
  int rank = _adept->talentRank(t);
  int circle = _adept->discipline().talentCircle(t);
  int legendCost = Database::talentLegendCost(rank+1,circle);
  _adept->setTalentRank(t,rank+1);
  _adept->decreaseLegendPoints(legendCost);

  _log << "[Talents] Passage du talent " << t << " au rang " << rank+1 << _log.endl;
}

bool AdeptEngine::canRaiseCircle()
{
  int newCircle = _adept->circle()+1;
  if (newCircle > 15) return false;

  // Minimum circle
  bool minimumOk = false;
  foreach(const Talent& t, _adept->talents())
  {
    if (_adept->discipline().talentCircle(t) == _adept->circle()
        && _adept->talentRank(t) >= Database::minimumTalentRankRequirement(newCircle))
    {
      minimumOk = true;
      break;
    }
  }
  if (!minimumOk) return false;

  // Talent count
  int count = 0;
  foreach(const Talent& t, _adept->talents())
  {
    if (_adept->talentRank(t) >= Database::minimumTalentRankRequirement(newCircle)) ++count;
  }
  return (count >= Database::minimumTalentCountRequirement(newCircle));
}
